#include "stdafx.h"
#include "isapi_python.h"
#include "HttpContext.h"
#include "ThreadLocalStorage.h"

using namespace std;
using namespace ISAPI_Python::Win32Helpers;

HttpContext::HttpContext(LPEXTENSION_CONTROL_BLOCK lpECB) throw()
		: InputDataOffset( 0 ),
		  Response( new HttpResponse() ),
		  Request( new HttpRequest() ),
		  ECB ( lpECB ) {

	ThreadLocalStorage::Current().Data()["HttpContext"] = this;
	
	Request->Context = this;
	Response->Context = this;
}

HttpContext::~HttpContext() throw() {
	ThreadLocalStorage::Current().Data().erase("HttpContext");

	delete Request;
	delete Response;
}


string HttpContext::GetServerVariable(const string& variableName) {
	DWORD bufferSize = 512;

	string buffer("", bufferSize);

	//This call intentionally fails, so we can then use
	// the bufferSize it returns by ref to allocate a buffer
	// that is Just Right.
	if(ECB->GetServerVariable(ECB->ConnID, (LPSTR)variableName.c_str(), (LPVOID)buffer.c_str(), &bufferSize)) {
		buffer.resize(bufferSize);
	} else {
		DWORD lastError = GetLastError();

		if(ERROR_INSUFFICIENT_BUFFER == lastError)  {
			buffer.resize(bufferSize);
			if(!ECB->GetServerVariable(ECB->ConnID, (LPSTR)variableName.c_str(), (LPVOID)buffer.c_str(), &bufferSize)) {
				throw Win32Exception();
			}
		} else if(ERROR_INVALID_INDEX == lastError) { 
			buffer.resize(0);
		} else {
			throw Win32Exception();
		}
	}

	return buffer;
}

vector<BYTE> HttpContext::GetRequestBytes(const size_t bytesNeeded) {
	if(ECB->cbTotalBytes == 0 || InputDataOffset > ECB->cbTotalBytes) {
		return vector<BYTE>(0);
	}

	vector<BYTE> output(bytesNeeded);

	size_t bytesAquired = 0;
	while(InputDataOffset < ECB->cbAvailable && bytesAquired <= bytesNeeded) {
		output[bytesAquired] = ECB->lpbData[InputDataOffset];
		
		bytesAquired++;
		InputDataOffset++;
	}

	if (ECB->cbTotalBytes > InputDataOffset && bytesAquired < bytesNeeded) {
		DWORD remainingBytesNeeded = bytesNeeded - bytesAquired;

		if(!ECB->ReadClient(ECB->ConnID, &output[bytesAquired], &remainingBytesNeeded)) {
			throw Win32Exception();
		}

		bytesAquired += remainingBytesNeeded;
	}

	if(bytesAquired < bytesNeeded) {
		output.resize(bytesAquired);
	}

	return output;
}

HttpContext* HttpContext::get_Current() throw() {
	LPVOID context = ThreadLocalStorage::Current().Data()["HttpContext"];
	return (HttpContext*)context;
}
